﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using AgilePlannerDT.Data;
using System.Windows.Media.Animation;
using AgilePlannerDT.Utils;
using AgilePlannerDT.Display.CustomWidgets;
using AgilePlannerDT.Data.Impl;
using AgilePlannerDT.Display.Events;
using AgilePlannerDT.Application;

namespace AgilePlannerDT.Display.Impl
{
    /// <summary>
    /// Interaction logic for StateAreaDisplayObjectImpl.xaml
    /// </summary>
    public partial class StateAreaDisplayObjectImpl : DisplayObjectImpl, IterationDisplayObject
    {
        #region Constants

        protected readonly double LOST_FOCUS_OPACITY = 0.45;

        #endregion

        #region Attributes

        protected List<StoryCardDisplayObject> storycards = new List<StoryCardDisplayObject>();
        protected Iteration iteration;

        #endregion

        #region Constructors

        public StateAreaDisplayObjectImpl(Iteration iteration, AgilePlannerApplication mainWindow)
            : base(mainWindow)
        {
            InitializeComponent();

            this.iteration = iteration;
            center = new Point(CardConstants.STATEAREA_DEFAULT_WIDTH / 2, CardConstants.STATEAREA_DEFAULT_HEIGHT / 2);
            isTossingEnabled = false;
            isRNTEnabled = true;        
            deleteIterationButton.Click += new RoutedEventHandler(deleteIterationButton_Click);
            DataContext = this;

            InitializeTransformations();
            InitializeAnimations();
            InitializeTextBoxes();
            InitializeDrawingArea();
            InitializeButtons();
            InitializeDataBinding();


            //create rotation around the center of the card
            rotate.CenterX = CardConstants.STATEAREA_DEFAULT_WIDTH / 2;
            rotate.CenterY = CardConstants.STATEAREA_DEFAULT_HEIGHT / 2;
            //move the statearea to the stored position
            trans.X = IterationData.LocationX * Properties.Settings.Default.Scale;
            trans.Y = IterationData.LocationY * Properties.Settings.Default.Scale;
            rotate.Angle = IterationData.RotationAngle;     

            iteration.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(Iteration_PropertyChanged);
            animationStoryBoard.Completed += new EventHandler(animationStoryBoard_Completed);
            pin.Opacity = 0;
            pin.Click += new RoutedEventHandler(pin_Click);

            //highlightTextBlock.Text = ID.ToString();
            highlightTextBlock.Opacity = 0;

            //set the context for the data bindings
           
            double dx = (iteration.Width - CardConstants.STATEAREA_DEFAULT_WIDTH);
            double dy = (iteration.Height - CardConstants.STATEAREA_DEFAULT_HEIGHT);
            this.Resize(ref dx, ref dy);
            this.rotate.CenterX = Width / 2;
            this.rotate.CenterY = Height / 2;

        }

        private void InitializeDataBinding()
        {
            Binding titleTextBoxBinding = new Binding("Description") { Source = iteration };
            this.titleTextBox.SetBinding(TextBox.TextProperty, titleTextBoxBinding);
        }

        void deleteIterationButton_Click(object sender, RoutedEventArgs e)
        {
            this.app.Delete(this);
            //	throw new NotImplementedException();
        }

        #endregion

        #region Visual Children Event Handling

        private void pin_Click(object sender, RoutedEventArgs e)
        {
            if (IsPinned)
            {
                IsPinned = false;       //TODO: Do we honestly need both of these?
                pin.Opacity = 0;
            }
            else
            {
                IsPinned = true;
                pin.Opacity = 1;
            }
        }

        private void VisualChildren_PreviewMouseMove(object o, RoutedEventArgs e)
        {
            OnMouseMove((MouseEventArgs)e);
        }

        private void VisualChildren_PreviewMouseLeftButtonDown(object o, RoutedEventArgs e)
        {
            OnMouseLeftButtonDown((MouseButtonEventArgs)e);
        }

        private void VisualChildren_PreviewMouseLeftButtonUp(object o, RoutedEventArgs e)
        {
            OnMouseLeftButtonUp((MouseButtonEventArgs)e);
        }

        //Highlight the textbox on focus
        private void TextBox_GotFocus(object sender, RoutedEventArgs e)
        {
            ((TextBox)sender).BorderThickness = new Thickness(2, 2, 2, 2);
            ((TextBox)sender).Background = Brushes.Black;
        }

        //Unhighlights the textbox on lost focus
        private void TextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            ((TextBox)sender).BorderThickness = new Thickness(0, 0, 0, 0);
            ((TextBox)sender).Background = Brushes.Transparent;
        }

        private void TextBox_ValidationError(object sender, ValidationErrorEventArgs e)
        {
            ((TextBox)sender).Undo();
        }

        //TODO: could rotation get changed here?
        private void Iteration_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            
        }

        /**
         * This section of code cycles the card's status sequentially
         * from Defined -> ToDo -> InProgress -> Complete -> Accepted
         * and updates the button's color appropriately.  For colors
         * and possible states, see CardConstants under Data.
         * 
         * I can't help but think that this could be done more quickly using
         * an enumeration.
         */
        private void statusButton_Click(object sender, RoutedEventArgs e)
        {
            if (IterationData.Status.Equals(CardConstants.STATUS_ACCEPTED))
            {
                UpdateStatusButton(CardConstants.STATUS_DEFINED, CardConstants.STATUS_DEFINED_COLOR);
            }
            else if (IterationData.Status.Equals(CardConstants.STATUS_IN_PROGRESS))
            {
                UpdateStatusButton(CardConstants.STATUS_COMPLETED, CardConstants.STATUS_COMPLETED_COLOR);
            }
            else if (IterationData.Status.Equals(CardConstants.STATUS_COMPLETED))
            {
                UpdateStatusButton(CardConstants.STATUS_ACCEPTED, CardConstants.STATUS_ACCEPTED_COLOR);
            }
            else if (IterationData.Status.Equals(CardConstants.STATUS_DEFINED))
            {
                UpdateStatusButton(CardConstants.STATUS_IN_PROGRESS, CardConstants.STATUS_IN_PROGRESS_COLOR);
            }
        }

        /*
         * Updates the StateArea's status and status button, as well
         * as the corresponding properties of each of its storycard children.
         */
        private void UpdateStatusButton(string status, Color color)
        {
            TextBlock tb;

            foreach (StoryCardDisplayObjectImpl sc in storycards)
            {
                tb = new TextBlock();
                tb.Text = status;
                tb.LayoutTransform = new RotateTransform(90, tb.ActualWidth / 2, tb.ActualHeight / 2);
                sc.statusButton.Background = CreateHorizontalGradientBrush(color);
                sc.StoryCardData.Status = status;
                sc.statusButton.Content = tb;
            }

            tb = new TextBlock();
            tb.Text = status;
            tb.LayoutTransform = new RotateTransform(90, tb.ActualWidth / 2, tb.ActualHeight / 2);
            statusButton.Background = CreateHorizontalGradientBrush(color);
            statusButton.Content = tb;
            IterationData.Status = status;
        }

        private Brush CreateHorizontalGradientBrush(Color color)
        {
            LinearGradientBrush brush = new LinearGradientBrush();
            brush.StartPoint = new Point(0, 0.5);
            brush.EndPoint = new Point(1, 0.5);

            brush.GradientStops.Add(new GradientStop(color, 0));
            brush.GradientStops.Add(new GradientStop(Color.FromArgb(0, color.R, color.G, color.B), 1));

            return brush;
        }

        #endregion

        #region Custom Event Handling

        protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
        {
            base.OnMouseDoubleClick(e);

            if (IsPinned)
            {
                IsPinned = false;
                pin.Opacity = 0;
            }
            else
            {
                IsPinned = true;
                pin.Opacity = 1;
            }
        }

        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);
            Point actual = e.GetPosition(this);
            app.BringToFront(this);
            IsActive = true;

            if (IsPinned)
                return;

            //for rotation (for comments see this method under StoryCardDisplayObjectImpl
            if (animationStoryBoard.GetCurrentState(this) == ClockState.Active)
            {
                rotate.Angle        = (double)rotate.GetValue(RotateTransform.AngleProperty);
                trans.X             = (double)trans.GetValue(TranslateTransform.XProperty);
                trans.Y             = (double)trans.GetValue(TranslateTransform.YProperty);

                animationStoryBoard.Stop(this);

                object before       = IterationData.Clone();
                Iteration after     = (Iteration)IterationData.Clone();

                after.LocationX     = trans.X / Properties.Settings.Default.Scale;
                after.LocationY     = trans.Y / Properties.Settings.Default.Scale;
                after.RotationAngle = rotate.Angle;

                IndexCardDropped(this, new IndexCardDropEventArgs(ID, ParentID, RelativeToAbsoluteCoordinates(actual), IterationData.RotationAngle, before, after));
            }

            //for rotation
            translateXAnimation.KeyFrames.Clear();
            translateYAnimation.KeyFrames.Clear();
            rotateAnimation.KeyFrames.Clear();

            IsTossed = false;

            //resize
            if (resizerHandle.IsInsideResizerHandleArea(e.GetPosition(resizerHandle)))
            {
                IsResized = true;
                IsTranslated = false;
                isDragged = false;
            }
            //RNT
            else
            {
                if (Math.Abs(e.GetPosition(this).X - center.X) < (this.Width)  / 4 &&
                    Math.Abs(e.GetPosition(this).Y - center.Y) < (this.Height) / 4)
                {
                    IsTranslated = true;   //cuts off rotation when close to center
                }
                else 
                {
                    IsTranslated = false;   //allows rotation
                }
                
                IsResized = false;
            }

            previous = RelativeToAbsoluteCoordinates(e.GetPosition(this));
            previousRelative = e.GetPosition(this);
        }

        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonUp(e);

            IsActive = false;

            if (IsPinned)
                return;

            Point actual = e.GetPosition(this);

            if (IsResized)
            {
                object before               = IterationData.Clone();
                Iteration after             = (Iteration)IterationData.Clone();

                after.Width = Width;
                after.Height = Height;
                this.IterationData.Width = Width;
                this.IterationData.Height = Height;
                IterationData.Notify(IndexCardDataObject.SIZE_PROPERTY, before, after);

                IsResized = false;
            }

            if (IsDragged)
            {
                Iteration before    = (Iteration)IterationData.Clone();
                IterationData.RotationAngle = rotate.Angle;
                Iteration after     = (Iteration)IterationData.Clone();

                //change the scale between display object and data object
                after.LocationX     = trans.X / Properties.Settings.Default.Scale;
                after.LocationY     = trans.Y / Properties.Settings.Default.Scale;

                IndexCardDropped(this, new IndexCardDropEventArgs(ID, ParentID, CenterAbsolute, rotate.Angle, before, after));

                IsDragged = false;
            }
        }
        
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (IsPinned)
                return;

            Point actual = RelativeToAbsoluteCoordinates(e.GetPosition(this));
            Point actualRelative = e.GetPosition(this);

            if (e.LeftButton.Equals(MouseButtonState.Pressed) && IsActive)
            {
                //ignore very small movements 
                //added to help with statusButton clicks, now that SAs rotate
                if (Util.GetDistance2D(previous, actual) < CardConstants.MIN_MOVEMENT_THRESHOLD / 4)
                {
                    previous = actual;
                    previousRelative = e.GetPosition(this);
                    AddMousePoint(actual);
                    AddTimePoint(DateTime.Now);
                    return;
                }

                if (IsResized)
                {
                    //determine the values for horizontal and vertical resize as
                    //the relative change between the previous mouse position and the actual
                    //on the card
                    double dx = actualRelative.X - previousRelative.X;
                    double dy = actualRelative.Y - previousRelative.Y;

                    Resize(ref dx, ref dy);
                    this.refitCardCenterAndAbsolutePosition(dx, dy);
                    (this.app).SynchronizeResizingCard(this, e.GetPosition((this.app).getCanvas()),0);
                }
                else
                {
                    IsDragged = true;
                    ApplyRNT(actual);
                    (this.app).SynchornizeMovingCard(this, e.GetPosition((this.app).getCanvas()),0);
                }

                AddMousePoint(actual);
                AddTimePoint(DateTime.Now);
                previous = actual;
                previousRelative = e.GetPosition(this);
            }
        }

        #endregion

        #region IterationDisplayObject Members

        public Iteration IterationData
        {
            get { return iteration; }
        }

        #endregion

        #region DisplayObject Members

        public override void Select()
        {
            if (IsSelected)
            {
                IsSelected = false;
                border.BorderBrush = Brushes.White;
                border.BorderThickness = new Thickness(1);
            }
            else
            {
                IsSelected = true;
                border.BorderBrush = new SolidColorBrush(CardConstants.CARD_SELECTED_BORDER);
                border.BorderThickness = new Thickness(3);
            }
        }

        public override void Highlight()
        {
            if (isHighlighted)
            {
                highlightTextBlock.Opacity = 0;
                isHighlighted = false;
            }
            else
            {
                highlightTextBlock.Opacity = 1;
                isHighlighted = true;
            }
        }

        public override long ID
        {
            get { return iteration.ID; }
        }

        public override long ParentID
        {
            get { return iteration.Parent; }
            set { iteration.Parent = value; }
        }

        public override void Resize(ref double dx, ref double dy)
        {
            //check if the resize would decrease the
            //size below minimum
           if (((Height + dy) >= MinHeight))
            {
                Height += dy;
                //   Width += dx;

                //     highlight.Width += dx;
                //       highlight.Height += dy;

                canvas.Height += dy;
                //       canvas.Width += dx;

                grid.Height += dy;


                //refit the center 
                center.Y += dy / 2;



            }
            else
            {
                dy = 0;
            }

            if (((Width + dx) >= MinWidth))
            {

                Width += dx;

                canvas.Width += dx;
                grid.Width += dx;

                //refit the center
                center.X += dx / 2;


            }
            else
            {
                dx = 0;
            }
            
            IsDragged = false;
        }
        



        public override void Move(double dx, double dy, double rotationAngle)
        {
            //Point prev1 = new Point(trans.X, trans.Y);
            //Point prev2;
            base.Move(dx, dy, rotationAngle);

            foreach (StoryCardDisplayObject sc in storycards)
            {
                //prev2 = new Point(sc.CenterAbsolute.X, sc.CenterAbsolute.Y);
                sc.Move(dx, dy, rotationAngle);
                //Console.WriteLine("center = (" + trans.X + ", " + trans.Y + ") sc = "
                //    + sc.CenterAbsolute.ToString() + "    " + Util.GetDistance2D(prev1, prev2) + " " +
                //    Util.GetDistance2D(new Point(trans.X, trans.Y), new Point(sc.CenterAbsolute.X, sc.CenterAbsolute.Y)));
            }
        }

        #endregion

        #region IterationDisplayObject Members

        public List<StoryCardDisplayObject> StoryCardChildren
        {
            get { return storycards; }
            set { storycards = value; }
        }

        public void AddStoryCard(StoryCardDisplayObject storycard)
        {
            storycard.State_Override(true);
            storycards.Add(storycard);

            if(IterationData.Status.Equals(CardConstants.STATUS_DEFINED))
            {
                UpdateStatusButton(CardConstants.STATUS_DEFINED, CardConstants.STATUS_DEFINED_COLOR);
            }
            else if (IterationData.Status.Equals(CardConstants.STATUS_IN_PROGRESS))
            {
                UpdateStatusButton(CardConstants.STATUS_IN_PROGRESS, CardConstants.STATUS_IN_PROGRESS_COLOR);
            }
            else if (IterationData.Status.Equals(CardConstants.STATUS_COMPLETED))
            {
                UpdateStatusButton(CardConstants.STATUS_COMPLETED, CardConstants.STATUS_COMPLETED_COLOR);
            }
            else if (IterationData.Status.Equals(CardConstants.STATUS_ACCEPTED))
            {
                UpdateStatusButton(CardConstants.STATUS_ACCEPTED, CardConstants.STATUS_ACCEPTED_COLOR);
            }
            
            BringToFront();
        }

        public void RemoveStoryCard(StoryCardDisplayObject storycard)
        {
            storycard.State_Override(false);
            storycards.Remove(storycard);
        }

        public void UpdateRemainingEffort()
        {
        }

        public override void AnimatedMoveTo(double dx, double dy, double drotationAngle)
        {
            base.AnimatedMoveTo(this.LocationX + dx, this.LocationY + dy, this.RotationAngle + drotationAngle);
            foreach (StoryCardDisplayObject sc in storycards)
            {
                //sc.Move(dx, dy);
                ((StoryCardDisplayObjectImpl)sc).setTriggerDropEventAfterAnimiationComplete(false);
                sc.AnimatedMoveTo(sc.LocationX + dx, sc.LocationY + dy, sc.RotationAngle + drotationAngle);
               // ((StoryCardDisplayObjectImpl)sc).setTriggerDropEventAfterAnimiationComplete(true);
            }
        }

        private void animationStoryBoard_Completed(object sender, EventArgs e)
        {
            if (translateXAnimation.KeyFrames.Count > 0)
                trans.X = translateXAnimation.KeyFrames[translateXAnimation.KeyFrames.Count - 1].Value;

            if (translateYAnimation.KeyFrames.Count > 0)
                trans.Y = translateYAnimation.KeyFrames[translateYAnimation.KeyFrames.Count - 1].Value;

            //if the card was only translated you don't have to update the rotation angle!
            if (rotateAnimation.KeyFrames.Count > 0)
                rotate.Angle = rotateAnimation.KeyFrames[rotateAnimation.KeyFrames.Count - 1].Value;

            //clear the keyframes, they are no longer needed
            translateXAnimation.KeyFrames.Clear();
            translateYAnimation.KeyFrames.Clear();
            rotateAnimation.KeyFrames.Clear();
        }

        #endregion

        #region Initializations

        private void InitializeTextBoxes()
        {
            /*textboxes.Add(titleTextBox);

            foreach (TextBox tb in textboxes)
                InitTextBox(tb);
             */
            InitTextBox(titleTextBox); 
            titleTextBox.Text = this.IterationData.Description;

            TextBlock tb = new TextBlock();
            tb.Text = IterationData.Status;
            tb.LayoutTransform = new RotateTransform(90, tb.ActualWidth / 2, tb.ActualHeight / 2);
            statusButton.Content = tb;
        }
        
        /// <summary>
        /// Sets up the Status button
        /// </summary>
        private void InitializeButtons()
        {
            if (IterationData.Status.Equals(CardConstants.STATUS_DEFINED))
                statusButton.Background = CreateHorizontalGradientBrush(CardConstants.STATUS_DEFINED_COLOR);
            else if (IterationData.Status.Equals(CardConstants.STATUS_IN_PROGRESS))
                statusButton.Background = CreateHorizontalGradientBrush(CardConstants.STATUS_IN_PROGRESS_COLOR);
            else if (IterationData.Status.Equals(CardConstants.STATUS_COMPLETED))
                statusButton.Background = CreateHorizontalGradientBrush(CardConstants.STATUS_COMPLETED_COLOR);
            else
                statusButton.Background = CreateHorizontalGradientBrush(CardConstants.STATUS_ACCEPTED_COLOR);

            statusButton.PreviewMouseMove += new MouseEventHandler(VisualChildren_PreviewMouseMove);
            statusButton.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(VisualChildren_PreviewMouseLeftButtonDown);
            statusButton.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(VisualChildren_PreviewMouseLeftButtonUp);
            statusButton.Click += new RoutedEventHandler(statusButton_Click);
        }

        private void InitializeDrawingArea()
        {
            MinHeight = CardConstants.STATEAREA_DEFAULT_HEIGHT;
            MinWidth = CardConstants.STATEAREA_DEFAULT_WIDTH;
            //         border.Background = new SolidColorBrush(CardConstants.BLUE);
            border.Background = new SolidColorBrush(CardConstants.STATUSAREA_BACKGROUND);
        }

        private void InitTextBox(TextBox tb)
        {
            tb.GotFocus += new RoutedEventHandler(TextBox_GotFocus);
            tb.LostFocus += new RoutedEventHandler(TextBox_LostFocus);

            tb.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(VisualChildren_PreviewMouseLeftButtonDown);
            tb.PreviewMouseMove += new MouseEventHandler(VisualChildren_PreviewMouseMove);
            tb.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(VisualChildren_PreviewMouseLeftButtonUp);

            tb.BorderThickness = new Thickness(0);
            tb.Background = border.Background;
        }

        #endregion
        
        #region Automation

        /// <summary>
        /// Provides an implementation of this control as per the WPF structure.
        /// </summary>
        /// <returns>Returns a new AutomationPeer for this type.</returns>
        protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
        {
            return new StateAreaDisplayObjectImplAutomationPeer(this);
        }

        public class StateAreaDisplayObjectImplAutomationPeer : DisplayObjectImplAutomationPeer
        {
            StateAreaDisplayObjectImpl host;

            /// <summary>
            /// Inits a new instance from the base class.
            /// </summary>
            /// <param name="control">The calling control.</param>
            public StateAreaDisplayObjectImplAutomationPeer(StateAreaDisplayObjectImpl control)
                : base(control)
            {
                host = control;
            }

            /// <summary>
            /// This is the name of the UIElement associated with the AutomationPeer.
            /// </summary>
            /// <returns>The name of this control type.</returns>
            protected override string GetClassNameCore()
            {
                return "StateAreaDisplayObjectImpl";
            }

            /// <summary>
            /// Sets the AutomationID
            /// </summary>
            /// <returns>The name of this display object.</returns>
            protected override string GetAutomationIdCore()
            {
                return "StateArea:" + host.ID;
            }

            protected override string GetHelpTextCore()
            {
                return
                      "\n\tActualEffort="           + host.IterationData.ActualEffort
                    + "\n\tAvailableEffort="        + host.IterationData.AvailableEffort
                    + "\n\tBestCaseEstimate="       + host.IterationData.BestCaseEstimate
                    + "\n\tDescription="            + host.IterationData.Description
                    + "\n\tEndDate="                + host.IterationData.EndDate
                    + "\n\tHeight="                 + host.IterationData.Height
                    //+ "\n\tID="                     + host.IterationData.ID
                    //+ "\n\tIsCompleted="            + host.IterationData.IsCompleted
                    //+ "\n\tIsStarted="              + host.IterationData.IsStarted
                    + "\n\tLocationX="              + host.IterationData.LocationX
                    + "\n\tLocationY="              + host.IterationData.LocationY
                    + "\n\tMostlikelyEstimate="     + host.IterationData.MostlikelyEstimate
                    + "\n\tName="                   + host.IterationData.Name
                    + "\n\tParent="                 + host.IterationData.Parent//This is going to be an issue
                    + "\n\tRemainingEffort="        + host.IterationData.RemainingEffort
                    + "\n\tRotationAngle="          + host.IterationData.RotationAngle
                    + "\n\tStartDate="              + host.IterationData.StartDate
                    + "\n\tStatus="                 + host.IterationData.Status
                    + "\n\tWidth="                  + host.IterationData.Width
                    + "\n\tWorstCaseEstimate="      + host.IterationData.WorstCaseEstimate
                    + "\n";
                    //+ "=" + host.IterationData.StoryCardChildren;
            }

            /// <summary>
            /// Expected behavior of a resize: cannot be done when pinned.
            /// </summary>
            public override bool CanResize
            {
                get
                {
                    return !host.isPinned;
                }
            }

            /// <summary>
            /// Moves the Iteration and triggers a dropped card event if successful.
            /// </summary>
            /// <param name="x">X-movement amount.</param>
            /// <param name="y">Y-movement amount.</param>
            public override void Move(double x, double y)
            {
                object before = host.IterationData.Clone();

                base.Move(x, y);

                object after = host.IterationData.Clone();

                ((Iteration)after).LocationX = host.trans.X / Properties.Settings.Default.Scale;
                ((Iteration)after).LocationY = host.trans.Y / Properties.Settings.Default.Scale;

                if (before != after)
                    host.IndexCardDropped(host, new IndexCardDropEventArgs(host.ID, host.ParentID,
                        host.CenterAbsolute, host.IterationData.RotationAngle, before, after));
            }

            /// <summary>
            /// Rotates the Iteration the specified number of degrees and propagates the change.
            /// </summary>
            /// <param name="degrees">The number of degrees by which to rotate the card.</param>
            public override void Rotate(double degrees)
            {
                object before = host.IterationData.Clone();

                base.Rotate(degrees);

                object after = host.IterationData.Clone();
                ((Iteration)after).RotationAngle = host.rotate.Angle;

                if (before != after)
                    host.IndexCardDropped(host, new IndexCardDropEventArgs(host.ID, host.ParentID,
                        host.CenterAbsolute, host.IterationData.RotationAngle, before, after));
            }

            /// <summary>
            /// Resizes the Iteration and propagates the change.
            /// </summary>
            /// <param name="width">The parameter by which the card should widen.</param>
            /// <param name="height">The parameter by which the card should heighten (sp?).</param>
            public override void Resize(double width, double height)
            {
                object before = host.IterationData.Clone();
                object after = host.IterationData.Clone();

                base.Resize(width, height);

                if ((int)(((Iteration)before).Width) != (int)host.Width ||
                    (int)(((Iteration)before).Height) != (int)host.Height)
                {
                    host.IterationData.Width = host.Width;
                    host.IterationData.Height = host.Height;

                    ((Iteration)after).Width = host.Width;
                    ((Iteration)after).Height = host.Height;

                    if (before != after)
                        host.IterationData.Notify(IndexCardDataObject.SIZE_PROPERTY, before, after);
                }
            }
        }

        #endregion

        #region MultiTouch Event

        public override void OnTouchDown(Point p)
        {
            base.OnTouchDown(p);

            Point actual = this.getPosition(p, this);
            app.BringToFront(this);
            IsActive = true;

            if (IsPinned)
                return;

            //for rotation (for comments see this method under StoryCardDisplayObjectImpl
            if (animationStoryBoard.GetCurrentState(this) == ClockState.Active)
            {
                rotate.Angle = (double)rotate.GetValue(RotateTransform.AngleProperty);
                trans.X = (double)trans.GetValue(TranslateTransform.XProperty);
                trans.Y = (double)trans.GetValue(TranslateTransform.YProperty);

                animationStoryBoard.Stop(this);

                object before = IterationData.Clone();
                Iteration after = (Iteration)IterationData.Clone();

                after.LocationX = trans.X / Properties.Settings.Default.Scale;
                after.LocationY = trans.Y / Properties.Settings.Default.Scale;
                after.RotationAngle = rotate.Angle;

                IndexCardDropped(this, new IndexCardDropEventArgs(ID, ParentID, RelativeToAbsoluteCoordinates(actual), IterationData.RotationAngle, before, after));
            }

            //for rotation
            translateXAnimation.KeyFrames.Clear();
            translateYAnimation.KeyFrames.Clear();
            rotateAnimation.KeyFrames.Clear();

            IsTossed = false;

            //resize
            if (resizerHandle.IsInsideResizerHandleArea(this.getPosition(p, resizerHandle)))
            {
                IsResized = true;
                IsTranslated = false;
                isDragged = false;
            }
            //RNT
            else
            {
                if (Math.Abs(this.getPosition(p, this).X - center.X) < (this.Width) / 4 &&
                    Math.Abs(this.getPosition(p, this).Y - center.Y) < (this.Height) / 4)
                {
                    IsTranslated = true;   //cuts off rotation when close to center
                }
                else
                {
                    IsTranslated = false;   //allows rotation
                }
                IsResized = false;
            }

            previous = p;
            previousRelative = this.getPosition(p, this);
        }

        public override void OnTouchMove(Point p)
        {
            base.OnTouchMove(p);

            if (IsPinned)
                return;

            Point actual = p;
            Point actualRelative = this.getPosition(p, this);

            if (IsActive)
            {
                if (IsResized)
                {
                    //determine the values for horizontal and vertical resize as
                    //the relative change between the previous mouse position and the actual
                    //on the card
                    double dx = actualRelative.X - previousRelative.X;
                    double dy = actualRelative.Y - previousRelative.Y;

                    Resize(ref dx, ref dy);
                    this.refitCardCenterAndAbsolutePosition(dx, dy);
                    //      ((AgilePlannerMainWindow)this.app).SynchronizeResizingCard(this, e);
                }
                else
                {
                    IsDragged = true;
                    ApplyRNT(actual);
                    //       ((AgilePlannerMainWindow)this.app).SynchornizeMovingCard(this, e);
                }

                AddMousePoint(actual);
                AddTimePoint(DateTime.Now);
                previous = actual;
                previousRelative = this.getPosition(p, this);
            }
        }

        public override void OnTouchUp(Point p)
        {
            base.OnTouchUp(p);

            IsActive = false;

            if (IsPinned)
                return;

            Point actual = this.getPosition(p, this);

            if (IsResized)
            {
                object before = IterationData.Clone();
                Iteration after = (Iteration)IterationData.Clone();

                after.Width = Width;
                after.Height = Height;
                this.IterationData.Width = Width;
                this.IterationData.Height = Height;
                IterationData.Notify(IndexCardDataObject.SIZE_PROPERTY, before, after);

                IsResized = false;
                return;     //not entirely necessary, but is a safeguard
            }

            if (IsDragged)
            {
                Iteration before = (Iteration)IterationData.Clone();
                Iteration after = (Iteration)IterationData.Clone();

                //change the scale between display object and data object
                after.LocationX = trans.X / Properties.Settings.Default.Scale;
                after.LocationY = trans.Y / Properties.Settings.Default.Scale;
                after.RotationAngle = rotate.Angle;

                IterationData.RotationAngle = rotate.Angle;//actually changes the rotation - for some reason this wasn't happening otherwise
                IndexCardDropped(this, new IndexCardDropEventArgs(ID, ParentID, CenterAbsolute, rotate.Angle, before, after));

                //double offsetX = after.LocationX - before.LocationX;
                //double offsetY = after.LocationY - before.LocationY;

                //foreach (StoryCardDisplayObject sc in StoryCardChildren)
                //{
                //    object bef = sc.StoryCardData.Clone();
                //    StoryCard aft = (StoryCard)sc.StoryCardData.Clone();

                //    aft.LocationX += offsetX;
                //    aft.LocationY += offsetY;

                //    IndexCardDropped(sc, new IndexCardDropEventArgs(sc.ID, sc.ParentID, sc.CenterAbsolute, sc.RotationAngle, bef, aft));
                //}

                IsDragged = false;
            }
        }
        #endregion

    }
}
